package com.youlu.campus.admin.auth.service.impl;

import com.alibaba.fastjson.JSON;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.mongodb.client.result.DeleteResult;
import com.youlu.campus.admin.auth.dto.RoleQueryRequest;
import com.youlu.campus.admin.auth.entity.*;
import com.youlu.campus.admin.auth.entity.exception.AuthErrorCode;
import com.youlu.campus.admin.auth.service.RoleService;
import com.youlu.campus.admin.auth.shiro.exception.AuthException;
import com.youlu.campus.admin.auth.vo.RoleVo;
import com.youlu.campus.admin.common.LoggerService;
import com.youlu.campus.common.exception.BusinessException;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.SetUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * Created by zhuhuaiqi
 */
@Service("roleService")
public class RoleServiceImpl extends LoggerService implements RoleService {
    @Autowired
    private MongoTemplate mongoTemplate;

    @PostConstruct
    public void init() {
        this.clearAllRoleCache();
    }

    @Override
    public List<SystemRole> listRole(String userId, String tenantId) {
        logger.info(":>>> 开始查询租户:{},下所有角色", tenantId);
        Query query = new Query(Criteria.where("tenantId").is(tenantId)).limit(10000);
        List<SystemRole> datas = mongoTemplate.find(query, SystemRole.class);
        return datas;
    }

    @Override
    public List<String> listRoleIdentifier(String userId, String tenantId) {
        logger.debug("listRoleIdentifier param userId:{}, tenantId:{}", userId, tenantId);
        Query query = new Query(Criteria.where("tenantId").is(tenantId)).limit(10000);
        List<SystemRole> roles = mongoTemplate.find(query, SystemRole.class);

        Query queryUserRole = new Query(Criteria.where("tenantId").is(tenantId).and("userId").is(userId)).limit(10000);
        List<SystemRoleUser> roleUsers = mongoTemplate.find(queryUserRole, SystemRoleUser.class);
        List<String> roleIds = new ArrayList<>();
        for (SystemRoleUser systemRoleUser : roleUsers) {
            if (roleIds.contains(systemRoleUser.getRoleId())) {
                roleIds.add(systemRoleUser.getRoleId());
            }

        }
        List<String> roleIdentifier = FluentIterable.from(roles).filter(new Predicate<SystemRole>() {

            @Override
            public boolean apply(SystemRole input) {
                return roleIds.contains(input.getId());
            }
        }).transform(new Function<SystemRole, String>() {
            @Override
            public String apply(SystemRole input) {
                return input.getIdentifier();
            }
        }).toList();
        logger.debug("listRoleIdentifier :{}", JSON.toJSONString(roleIdentifier));
        return roleIdentifier;
    }


    @Override
    public List<String> listRoleByUserIdAndComId(String userId, String comId) {
        logger.debug("listRoleByUserIdAndComId param userId:{}, comId:{}", userId, comId);
        List<String> roleIds = new ArrayList<>();
        roleIds = listRoleByUserIdAndComIdFromDb(userId, comId);
        return roleIds;
    }

    @Override
    public void clearAllRoleCache() {
//        jc.del(getRoleAllRedisKey());
    }

    @Override
    public boolean isRole(SystemUser user, String roleIdentifier) {
        List<String> roles = this.listRoleIdentifier(user.getId(), user.getTenantId());
        if (roles == null || roles.isEmpty()) {
            return false;
        }
        if (roles.contains(roleIdentifier)) {
            return true;
        }
        return false;
    }

    private List<String> listRoleByUserIdAndComIdFromDb(String userId, String tenantId) {
        logger.debug("listRoleIdentifier param userId:{}, tenantId:{}", userId, tenantId);

        Query queryUserRole = new Query(Criteria.where("tenantId").is(tenantId).and("userId").is(userId)).limit(10000);
        List<SystemRoleUser> roleUsers = mongoTemplate.find(queryUserRole, SystemRoleUser.class);
        logger.info(":>>> 用户角色映射tenantId:{},userId:{},值:{}", tenantId, userId, JSON.toJSONString(roleUsers));
        List<String> roleIds = new ArrayList<>();
        for (SystemRoleUser systemRoleUser : roleUsers) {
            if (!roleIds.contains(systemRoleUser.getRoleId())) {
                roleIds.add(systemRoleUser.getRoleId());
            }

        }
        logger.debug(":>>>  userId:{}, tenantId:{} 的角色ID集合:{}", userId, tenantId, JSON.toJSONString(roleIds));
        return roleIds;
    }

    /**
     * 使用guava工具类来取List集合的并集
     *
     * @param companyRoles 公司角色
     * @param userRoles    用户角色
     * @return 两个集合的差集
     */
    private List<String> getIntersectionRoles(List<String> companyRoles, List<String> userRoles) {
        Set<String> differenceSet = Sets.intersection(Sets.newHashSet(companyRoles), Sets.newHashSet(userRoles));
        return Lists.newArrayList(differenceSet);
    }

    @Override
    public void initCompanyRoles(String tenentId) {
        logger.debug("初始化1~4四个角色 & 角色6");
        RoleIdentifier.values();
        for (RoleIdentifier r : RoleIdentifier.values()) {
            SystemRole role = new SystemRole();
            role.setTenantId(tenentId);
            role.setIdentifier(r.getIdentifier());
            role.setCreatedTime(new Date());
            mongoTemplate.insert(role);
        }
    }


    @Override
    public List<String> getTenantRoleIds(String tenantId) {
        logger.debug(":>>> getTenantRoleIds param tenantId:{}", tenantId);
        Query query = new Query(Criteria.where("tenantId").is(tenantId)).limit(10000);
        List<SystemRole> roles = mongoTemplate.find(query, SystemRole.class);
        List<String> roleIds = new ArrayList<>();
        for (SystemRole systemRole : roles) {
            if (!roleIds.contains(systemRole.getId())) {
                roleIds.add(systemRole.getId());
            }
        }
        return roleIds;
    }

    @Override
    public SystemRole get(String tenantId, String roleIdentifier) {
        logger.info(":>>> 开始获取角色:{},{}", tenantId, roleIdentifier);
        if (StringUtils.isBlank(tenantId) || StringUtils.isBlank(roleIdentifier)) {
            logger.error(":>>> 租户ID或角色标识为空");
            return null;
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("tenantId").is(tenantId).and("identifier").is(roleIdentifier));
        return mongoTemplate.findOne(query, SystemRole.class);
    }

    @Override
    public Page<SystemRole> query(RoleQueryRequest roleQueryRequest) {
        logger.info(":>>> 开始获取角色列表:{}", JSON.toJSONString(roleQueryRequest));
        if (Objects.isNull(roleQueryRequest)) {
            logger.error(":>>> 参数错误,请求参数为空");
            throw new AuthException(AuthErrorCode.INVALID_ARGUMENT);
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("tenantId").is(roleQueryRequest.getTenantId()));

        if (StringUtils.isNotBlank(roleQueryRequest.getRoleName())) {
            Pattern pattern = Pattern.compile("^.*" + roleQueryRequest.getRoleName() + ".*$", Pattern.CASE_INSENSITIVE);
            query.addCriteria(Criteria.where("name").regex(pattern));
        }
        long count = mongoTemplate.count(query, SystemRole.class);
        PageRequest pageRequest = PageRequest.of(roleQueryRequest.getPage() - 1, roleQueryRequest.getPageSize());
//        query.with(new Sort(Sort.Direction.DESC, "createdTime"));
        Sort sort=Sort.by(Sort.Direction.DESC, "createdTime");
        query.with(sort);
        List<SystemRole> datas = mongoTemplate.find(query.with(pageRequest), SystemRole.class);
        return PageableExecutionUtils.getPage(datas, pageRequest, () -> count);
    }

    @Override
    public boolean create(RoleVo roleVo) {
        logger.info(":>>> 开始创建角色:{}", JSON.toJSONString(roleVo));
        if (Objects.isNull(roleVo) || StringUtils.isBlank(roleVo.getName())
                || StringUtils.isBlank(roleVo.getDescription()) || StringUtils.isBlank(roleVo.getTenantId()) ||
                CollectionUtils.isEmpty(roleVo.getPermissionIds())) {
            logger.error(":>>> 创建角色参数错误");
            throw new BusinessException(AuthErrorCode.INVALID_ARGUMENT);
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("tenantId").is(roleVo.getTenantId()).and("name").is(roleVo.getName()));
        boolean exist = mongoTemplate.exists(query, SystemRole.class);
        if (exist) {
            logger.error(":>>> 创建角色错误:{} 已经存在", roleVo.getName());
            throw new BusinessException(AuthErrorCode.DATA_ALREADY_EXIST);
        }
        SystemRole role = new SystemRole();
        role.setTenantId(roleVo.getTenantId());
        role.setDescription(roleVo.getDescription());
        role.setName(roleVo.getName());
        role.setCreatedTime(new Date());
        role.setPermissionIds(roleVo.getPermissionIds());
        role.setMobileDesensitization(roleVo.getMobileDesensitization());
        role.setMobileHide(roleVo.getMobileHide());
        role = mongoTemplate.insert(role);

        logger.info(":>>> 创建的角色:{},ID:{}", role.getName(), role.getId());

        this.handlePermissionIds(roleVo.getTenantId(), role.getId(), roleVo.getPermissionIds());
//        Iterator<Menu> iterator = roleVo.getPermissions().iterator();
//        while (iterator.hasNext()) {
//            Menu menu = iterator.next();
//            handlePermission(roleVo.getTenantId(), role.getId(), menu);
//        }
        return true;
    }

    @Override
    public boolean update(RoleVo roleVo) {
        logger.info(":>>> 开始修改角色:{}", JSON.toJSONString(roleVo));
        if (Objects.isNull(roleVo) || StringUtils.isBlank(roleVo.getId()) || StringUtils.isBlank(roleVo.getName())
                || StringUtils.isBlank(roleVo.getDescription()) || StringUtils.isBlank(roleVo.getTenantId()) ||
                CollectionUtils.isEmpty(roleVo.getPermissionIds())) {
            logger.error(":>>> 修改角色参数错误");
            throw new BusinessException(AuthErrorCode.INVALID_ARGUMENT);
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("tenantId").is(roleVo.getTenantId()).and("id").is(roleVo.getId()));
        boolean exist = mongoTemplate.exists(query, SystemRole.class);
        if (!exist) {
            logger.error(":>>> 修改角色错误:{} 已经存在", roleVo.getName());
            throw new BusinessException(AuthErrorCode.DATA_NOT_EXIST);
        }
        Update update = new Update();
        update.set("name", roleVo.getName());
        update.set("description", roleVo.getDescription());
        update.set("updatedTime", new Date());
        update.set("permissionIds", roleVo.getPermissionIds());
        update.set("mobileDesensitization", roleVo.getMobileDesensitization());
        update.set("mobileHide", roleVo.getMobileHide());
        mongoTemplate.updateFirst(query, update, SystemRole.class);

        Query query1 = new Query();
        query1.addCriteria(Criteria.where("tenantId").is(roleVo.getTenantId()).and("roleId").is(roleVo.getId()));
        DeleteResult deleteResult = mongoTemplate.remove(query1, SystemRoleResouce.class);
        logger.info(":>>> 删除角色:{} 和资源的映射条数:{}", roleVo.getId(), deleteResult.getDeletedCount());
        this.handlePermissionIds(roleVo.getTenantId(), roleVo.getId(), roleVo.getPermissionIds());
//        Iterator<Menu> iterator = roleVo.getPermissions().iterator();
//        while (iterator.hasNext()) {
//            Menu menu = iterator.next();
//            handlePermission(roleVo.getTenantId(), roleVo.getId(), menu);
//        }
        return true;
    }

    @Override
    public SystemRole getById(String id, String tenantId) {
        Query queryRes = new Query();
        queryRes.addCriteria(Criteria.where("tenantId").is(tenantId).and("id").is(id));
        SystemRole role = mongoTemplate.findOne(queryRes, SystemRole.class);
        return role;
    }

    @Override
    public Set<String> getTenantRoleName(String tenantId) {
        Query query = new Query(Criteria.where("tenantId").is(tenantId)).limit(10000);
        List<SystemRole> roles = mongoTemplate.find(query, SystemRole.class);
        if(CollectionUtils.isEmpty(roles)){
          return SetUtils.emptySet();
        }
        return roles.stream().map(SystemRole::getName).collect(Collectors.toSet());
    }

    @Override
    public List<SystemRole> findByRoleIdList(List<String> queryRoleIdList) {
        Query query = new Query();
        query.addCriteria(Criteria.where("id").in(queryRoleIdList));
        return this.mongoTemplate.find(query,SystemRole.class);
    }

    @Override
    public List<SystemRole> findAllByUserId(String userId) {
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(userId));
        List<SystemRoleUser> roleUsers = this.mongoTemplate.find(query, SystemRoleUser.class);
        if(CollectionUtils.isEmpty(roleUsers)){
            return Collections.emptyList();
        }
        return this.findByRoleIdList(roleUsers.stream().map(SystemRoleUser::getRoleId).collect(Collectors.toList()));
    }

    private void handlePermission(final String tenantId, final String roleId, Menu menu) {
        final String rsId = menu.getId();
        Query queryRes = new Query();
        queryRes.addCriteria(Criteria.where("tenantId").is(tenantId).and("id").is(rsId));
        boolean rsExist = mongoTemplate.exists(queryRes, SystemResource.class);
        if (rsExist) {
            SystemRoleResouce systemRoleResouce = new SystemRoleResouce();
            systemRoleResouce.setTenantId(tenantId);
            systemRoleResouce.setCreatedTime(new Date());
            systemRoleResouce.setResourceId(rsId);
            systemRoleResouce.setRoleId(roleId);
            mongoTemplate.insert(systemRoleResouce);
            logger.info(":>>> 插入租户:{},角色:{},资源:{}", tenantId, roleId, rsId);
        } else {
            logger.info(":>>> 资源:{} 不存在", rsId);
        }
        for (Menu menu1 : menu.getChilds()) {
            handlePermission(tenantId, roleId, menu1);
        }

    }

    private void handlePermissionIds(final String tenantId, final String roleId, List<String> permissionIds) {
        logger.info(":>>> 开始处理权限,tenantId:{},roleId:{},permissionIds:{}", tenantId, roleId, JSON.toJSONString(permissionIds));
        if (StringUtils.isBlank(tenantId) || StringUtils.isBlank(roleId) || CollectionUtils.isEmpty(permissionIds)) {
            logger.info(":>>> 处理权限参数错误");
            return;
        }
//        List<String> permissionIdTodos = permissionIds.stream().distinct().collect(Collectors.toList());
        logger.debug(":>>> 去重之前长度:{},去重之后长度:{}", permissionIds.size(), permissionIds.size());
        List<SystemRoleResouce> inserts = new ArrayList<>();
        Date date = new Date();
        for (String resId : permissionIds) {
            SystemRoleResouce systemRoleResouce = new SystemRoleResouce();
            systemRoleResouce.setTenantId(tenantId);
            systemRoleResouce.setCreatedTime(date);
            systemRoleResouce.setResourceId(resId);
            systemRoleResouce.setRoleId(roleId);
            inserts.add(systemRoleResouce);
        }
        mongoTemplate.insertAll(inserts);
    }


}
